#pragma once
#include <leveldb/db.h>

#include <iostream>
#include <memory>
#include <string>

struct DBOptions {
  bool create_if_missing;
  DBOptions(bool create_if_missing = true)
      : create_if_missing(create_if_missing) {}
};

class DBIterator {
 public:
  // construct
  DBIterator(leveldb::Iterator* it) noexcept { this->it_.reset(it); }
  // copy
  DBIterator(const DBIterator& other) = delete;
  DBIterator& operator=(const DBIterator& rhs) = delete;
  // move
  DBIterator(DBIterator&& other) noexcept : it_(std::move(other.it_)) {}
  DBIterator& operator=(DBIterator&& rhs) noexcept {
    if (this != &rhs) {
      it_ = std::move(rhs.it_);
    }
    return *this;
  }
  // destruct
  ~DBIterator() noexcept = default;

  bool Valid() { return it_->Valid(); }
  void SeekToFirst() { it_->SeekToFirst(); }
  void Next() { it_->Next(); }
  std::string key() { return it_->key().ToString(); }
  std::string value() { return it_->value().ToString(); }

 private:
  std::unique_ptr<leveldb::Iterator> it_;
};

class DBWrapper {
 public:
  // construct
  DBWrapper(const DBOptions& opt, std::string path) noexcept;
  // copy
  DBWrapper(const DBWrapper& other) = delete;
  DBWrapper& operator=(const DBWrapper& rhs) = delete;
  // move
  DBWrapper(DBWrapper&& other) noexcept;
  DBWrapper& operator=(DBWrapper&& rhs) noexcept;
  // destruct
  ~DBWrapper() noexcept = default;

  bool isValid();
  bool Put(const std::string& key, const std::string& value);
  std::string Get(const std::string& key);
  bool Del(const std::string& key);
  DBIterator Iterator();

 private:
  leveldb::DB* Open(const DBOptions& opt);
  std::unique_ptr<leveldb::DB> db_;
  std::string db_path_;
};
